Fix crash when a call signature's type parameter cannot be reused - #64017
Open
Nicolaev Eduard (nikeedw) wants to merge 2 commits into
Open
Fix crash when a call signature's type parameter cannot be reused#64017Nicolaev Eduard (nikeedw) wants to merge 2 commits into
Nicolaev Eduard (nikeedw) wants to merge 2 commits into
Conversation
The PseudoTypeKindSingleCallSignature branch of pseudoTypeToNode appended the result of reuseNode into the type parameter list without checking it. reuseNode returns nil whenever the recovery boundary in tryReuseExistingNodeHelper fails, and that nil survived into the NodeList, where the printer dereferenced it in NodeList.HasTrailingComma while deciding whether to write a trailing comma. Serialize the type parameter from the checker instead, mirroring the fallback reuseTypeNode already performs for type nodes. Two regression scenarios: a mixed list where the first type parameter reuses fine and the second requires the fallback, and the object-literal method counterpart, which rewrites the same constraint successfully and pins that the sibling branch stays panic-free. Carried over from microsoft/typescript-go#4846 (approved there; repo migrated). Fixes microsoft#63865 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #63865.
The crash
The
PseudoTypeKindSingleCallSignaturebranch ofpseudoTypeToNodeappends the result ofreuseNodeinto a call signature's type parameter list without checking it:reuseNodereturns nil whenever the recovery boundary intryReuseExistingNodeHelperfails — e.g. the constraint references a name that is inaccessible from the file being emitted. The nil is stored in theNodeListand survives to the printer, which dereferences it inNodeList.HasTrailingCommawhile deciding whether to write a trailing comma. Every other caller ofreuseNodecopes with the nil; this call site did not.Why it only reproduces on the incremental path
For a plain build, files in this shape always carry declaration diagnostics (TS2527/TS4023), and
emitDeclarationFileskips printing a file that has them — the malformed list is built but never printed. The one production path that prints despite declaration diagnostics is the incremental d.ts shape-signature computation (EmitOnlyForcedDts, sole caller inaffectedfileshandler.go). A cold run hashes file text and never prints; the warm run computes the real signature, prints the malformed tree, and panics. That is also why the regression test is atsctestsscenario rather than a compiler test: verified in the original PR, the same fixture undercases/compileris green both with and without the fix.The original report came from a mobx-state-tree codebase where all of this falls out of ordinary usage (a generic setter in
.actions(self => ({ ... })), MST'sunique symbolbrands,types.composepulling models across files).The fix
Fall back to serializing the type parameter from the checker, mirroring what
reuseTypeNodealready does for type nodes.typeParameterToDeclarationalways returns a node, so the list can no longer contain a nil.Tests
Two
TestTscDeclarationEmitscenarios:dts signature update with type parameters that cannot be reused— a mixed list: the first type parameter (Tag extends string) reuses fine, the second (K extends keyof typeof state) requires the fallback, so the fallback is exercised past the first list slot and reused/serialized nodes coexist in one list. Without the fix it panics inNodeList.HasTrailingComma; with it the baseline shows the signature computed correctly and both declaration diagnostics still reported:dts signature update with a method type parameter that cannot be reused— the object-literal method counterpart. The method branch (PseudoObjectElementKindMethod) contains the same uncheckedreuseNodeappend, but I could not construct an input where its reuse actually fails — the method context rewrites the same constraint successfully. This scenario pins that behavior (green both before and after the fix), so the sibling site is deliberately left untouched rather than given an untestable fallback.Verification
main(the arrow scenario panics without the fix; the method scenario stays green).go test ./internal/...— 61/61 packages pass; no baseline drift outside the two new files.gofmt,go vet, and thetools/customlintanalyzers (includingshadow) — clean on the touched files.AI assistance disclosure
Per CONTRIBUTING.md: this patch was authored with the help of Claude Code. #63865 is my own bug report against my own codebase, I drove the investigation, I have read and understand the change, and I will be handling review feedback.